package com.ddpt.permission.service.login;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.lh.center.common.constant.GlobalConstant;
import com.lh.center.common.constant.RedisPrefix;
import com.lh.center.common.dto.LoginByPhoneDto;
import com.lh.center.common.enums.MerchantServiceType;
import com.lh.center.common.exception.CommonException;
import com.lh.center.common.exception.ErrorEnum;
import com.lh.center.common.util.AssertUtil;
import com.lh.center.common.web.session.SessionOperation;
import com.lh.center.merchant.api.dubbo.DubboMerchantInsService;
import com.lh.center.merchant.api.dubbo.DubboMerchantService;
import com.lh.center.merchant.api.entity.MerchantIns;
import com.lh.center.merchant.api.request.MerchantInsQueryRequest;
import com.lh.center.merchant.api.request.MerchantQueryRequest;
import com.lh.center.merchant.api.response.MerchantResponse;
import com.lh.center.permission.api.dubbo.DubboServiceModuleService;
import com.lh.center.permission.api.entity.MerchantAdminUser;
import com.lh.center.permission.api.entity.Permission;
import com.lh.center.permission.api.entity.Role;
import com.lh.center.permission.api.entity.RolePermission;
import com.lh.center.permission.api.response.ServiceModuleResponse;
import com.lh.center.permission.api.session.MerchantPermission;
import com.lh.center.permission.api.session.MerchantSessionUser;
import com.lh.center.permission.api.session.SessionPermission;
import com.lh.center.permission.mapper.PermissionMapper;
import com.lh.center.permission.service.*;
import com.lh.center.permission.util.ImageCodeUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service("merchantLoginService")
public class MerchantLoginServiceImpl implements LoginService<MerchantSessionUser> {

    @Value("${admin.timeout.merchant:30}")
    private Integer loginTimeout;

    @Resource
    private ImageCodeUtil imageCodeUtil;

    @DubboReference
    private DubboMerchantService dubboMerchantService;

    @DubboReference
    private DubboMerchantInsService dubboMerchantInsService;

    @Resource
    private PermissionMapper permissionMapper;

    @Resource
    private PermissionService permissionService;

    @Resource
    private RolePermissionService rolePermissionService;

    @Resource
    private RoleService roleService;

    @Resource
    private MerchantAdminUserService merchantAdminUserService;

    @DubboReference
    private DubboServiceModuleService serviceModuleService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    @Override
    public MerchantSessionUser loginByPassword(String account, String password, String imgCode, String codeId) {
        imageCodeUtil.verifyCode(codeId,imgCode);
        return loginByPassword(account, password);
    }

    public MerchantSessionUser loginByPasswordCallBack(String account, String password) {
        MerchantAdminUser adminUser = merchantAdminUserService.queryByUsernameAndPasswordCallBack(account, password);
        return loginByAdminUser(adminUser);
    }

    @Override
    public MerchantSessionUser loginByPassword(String account, String password) {
        MerchantAdminUser adminUser = merchantAdminUserService.queryByUsernameAndPassword(account, password);
        return loginByAdminUser(adminUser);
    }

    @Override
    public MerchantSessionUser autoLogin(Long merchantId, String phone, String randomSecret) {
        MerchantAdminUser user = merchantAdminUserService.get(Wrappers.lambdaQuery(MerchantAdminUser.class)
                .eq(MerchantAdminUser::getMerchantId, merchantId)
                .eq(MerchantAdminUser::getUsername, phone)
        );
        AssertUtil.isNull(user,"自动登陆失败,用户不存在");
        if(StringUtils.isNotBlank(user.getRandomSecret()) && !user.getRandomSecret().equals(randomSecret)){
            throw new CommonException("自动登陆失败,随机码错误");
        }
        user.setRandomSecret(UUID.randomUUID().toString().replace("-",""));
        merchantAdminUserService.updateById(user);
        return loginByAdminUser(user);
    }

    public MerchantSessionUser loginByAdminUser(MerchantAdminUser adminUser) {
        if(null == adminUser) {
            throw new CommonException(ErrorEnum.USERNAME_OR_PASSWORD_ERROR);
        }
        if(GlobalConstant.ZERO.equals(adminUser.getStatus())) {
            throw new CommonException(ErrorEnum.ADMIN_INVALID);
        }
        boolean superAdmin = GlobalConstant.ZERO.equals(adminUser.getType());
        Long merchantId = adminUser.getMerchantId();
        Long insId = adminUser.getInsId();
        Long roleId = adminUser.getRoleId();
        //普通管理员，校验角色状态
        if(!superAdmin) {
            Role role = roleService.getById(roleId);
            if(null == role || GlobalConstant.ONE.equals(role.getDeleted())) {
                throw new CommonException(ErrorEnum.CURRENT_ROLE_INVALID);
            }
        }
        //商户状态及服务有效期校验
        MerchantQueryRequest merchantQueryRequest = new MerchantQueryRequest();
        merchantQueryRequest.setId(adminUser.getMerchantId());
        MerchantResponse merchant = dubboMerchantService.queryDetail(merchantQueryRequest);
        if(null == merchant || GlobalConstant.ONE.equals(merchant.getDeleted()) || GlobalConstant.ZERO.equals(merchant.getStatus())) {
            throw new CommonException(ErrorEnum.MERCHANT_NOT_EXIST_OR_INVALID);
        }
        if(merchant.getExpireDate() <= System.currentTimeMillis()) {
            throw new CommonException(ErrorEnum.MERCHANT_EXPIRED);
        }
        //机构状态校验
        MerchantIns merchantIns = null;
        if(!superAdmin) {
            merchantIns = dubboMerchantInsService.queryById(insId);
            if(null == merchantIns || GlobalConstant.ZERO.equals(merchantIns.getStatus())) {
                throw new CommonException(ErrorEnum.MERCHANT_INS_NOT_EXIST_OR_INVALID);
            }
        }
        MerchantSessionUser sessionUser = new MerchantSessionUser();
        //权限收集
        List<SessionPermission> permissionList;
        List<MerchantServiceType> merchantServiceList = merchant.getServiceList(),insServiceList;
        //超管，查询该商户拥有的所有应用权限
        if(superAdmin) {
            permissionList = permissionMapper.queryMerchantPermissionByMerchantService(merchantServiceList);
            insServiceList = merchantServiceList;
            MerchantInsQueryRequest insQueryRequest = new MerchantInsQueryRequest();
            insQueryRequest.setId(insId);
            insQueryRequest.setMerchantId(merchantId);
            List<MerchantIns> insList = dubboMerchantInsService.recursionQueryToList(insQueryRequest);
            sessionUser.setDataInsIds(insList.stream().map(MerchantIns::getId).collect(Collectors.toList()));
            sessionUser.setApiPaths(new HashSet<>(Collections.singleton("/**")));
        } else {
            Integer dataAuth = adminUser.getDataAuth();
            if(dataAuth == 1) {
                sessionUser.setDataInsIds(Collections.singletonList(insId));
            } else if(dataAuth == 2) {
                MerchantInsQueryRequest insQueryRequest = new MerchantInsQueryRequest();
                insQueryRequest.setId(insId);
                insQueryRequest.setMerchantId(merchantId);
                List<MerchantIns> insList = dubboMerchantInsService.recursionQueryToList(insQueryRequest);
                sessionUser.setDataInsIds(insList.stream().map(MerchantIns::getId).collect(Collectors.toList()));
            } else {
                sessionUser.setDataEmpIds(Collections.singletonList(adminUser.getId()));
                sessionUser.setDataInsIds(Collections.singletonList(insId));
            }
            insServiceList = MerchantServiceType.getServiceList(merchantIns.getServices(), ",");
            if(CollUtil.isEmpty(insServiceList)) {
                throw new CommonException(ErrorEnum.INS_HVAE_NO_SERVICE);
            }
            //取交集时顺序不能颠倒，否则可能导致跳转到错误的应用
            insServiceList.retainAll(merchantServiceList);
            permissionList = permissionMapper.queryMerchantPermissionByRoleAndMerchantService(roleId,insServiceList);
        }
        List<Long> dataInsIds = sessionUser.getDataInsIds();
        if(CollUtil.isNotEmpty(dataInsIds)) {
            sessionUser.setDataEmpIds(merchantAdminUserService.queryIdByIns(dataInsIds));
        }
        if(CollUtil.isEmpty(permissionList)) {
            throw new CommonException(ErrorEnum.HAVE_NONE_PERMISSION);
        }
        Map<MerchantServiceType, List<SessionPermission>> permissionMap = permissionList.stream().collect(Collectors.groupingBy(SessionPermission::getMerchantService));
        List<MerchantPermission> allPermission = new ArrayList<>();
        List<SessionPermission> cur;
        MerchantPermission curPermission;
        ServiceModuleResponse curModule;
        List<ServiceModuleResponse> moduleList = serviceModuleService.queryModuleList(true);
        Map<String, ServiceModuleResponse> moduleMap = moduleList.stream().collect(Collectors.toMap(ServiceModuleResponse::getCode, Function.identity()));
        for(MerchantServiceType serviceType : insServiceList) {
            curPermission = new MerchantPermission();
            curPermission.setServiceType(serviceType);
            curPermission.setIcon((curModule = moduleMap.get(serviceType.getCode())).getIcon());
            curPermission.setCheckedIcon(curModule.getCheckedIcon());
            if(CollUtil.isNotEmpty(cur = permissionMap.get(serviceType))) {
//                curPermission.setPermissionList(cur);
                curPermission.setPermissionLayer(permissionService.buildLayerPermission(cur));
            }
            allPermission.add(curPermission);
        }
        MerchantServiceType serviceType = null;
        boolean containMerchant = insServiceList.contains(MerchantServiceType.MERCHANT);
        //持有商户系统权限且商户系统菜单不为空时跳向商户系统，否则跳向第一个菜单不为空的应用系统
        if(containMerchant && CollUtil.isNotEmpty(permissionMap.get(MerchantServiceType.MERCHANT))) {
            serviceType = MerchantServiceType.MERCHANT;
        } else {
            for(MerchantServiceType service : insServiceList) {
                if(CollUtil.isNotEmpty(permissionMap.get(service))) {
                    serviceType = service;
                    break;
                }
            }
        }
        sessionUser.setCurrentService(serviceType.name());
        sessionUser.setUserId(adminUser.getId());
        sessionUser.setMerchantId(merchantId);
        sessionUser.setUsername(adminUser.getUsername());
        sessionUser.setRealName(adminUser.getRealName());
        sessionUser.setAccount(adminUser.getUsername());
        sessionUser.setAdminType(adminUser.getType());
        sessionUser.setDataAuth(adminUser.getDataAuth());
        sessionUser.setBindWx(adminUser.getBindWechat());
        sessionUser.setBindWxPromptStatus(adminUser.getBindWxPromptStatus());
        sessionUser.setInsId(insId);
        sessionUser.setBackstageTradeId(merchant.getBackstageTradeId());
        String randomKey = UUID.randomUUID().toString();
        sessionUser.setSecret(RandomUtil.randomString(16));
        //超管机构的insId统一设为0
        insId = null == insId ? 0 : insId;
        String token = String.format("#%s#%s#%s#",merchantId,insId,adminUser.getId()) + randomKey;
        sessionUser.setToken(token);
        if(sessionUser.getDataInsIds() == null){
            sessionUser.setDataInsIds(new ArrayList<>());
        }
        if(!superAdmin){
            //非超管，确认接口权限
            Set<Long> permissionIds = rolePermissionService.lambdaQuery()
                    .select(RolePermission::getPermissionId)
                    .eq(RolePermission::getRoleId, roleId)
                    .list().stream().map(RolePermission::getPermissionId).collect(Collectors.toSet());
            List<String> apiPaths = permissionService.lambdaQuery()
                    .select(Permission::getApiPath)
                    .in(Permission::getId, permissionIds)
                    .list().stream().map(Permission::getApiPath).collect(Collectors.toList());
            Set<String> finalApiPaths = new HashSet<>();
            apiPaths.forEach(path -> {
                finalApiPaths.addAll(Stream.of(path.split(",")).filter(StringUtils::isNotBlank).collect(Collectors.toList()));
            });
            finalApiPaths.addAll(Arrays.asList(
                    "/api/permission/login/keepLive",
                    "/api/merchant/serviceModule/queryList",
                    "/api/merchant/press/pressPage",
                    "/api/merchant/merchantIns/queryTree",
                    "/api/merchant/merchantLog/getTokenUser",
                    "/api/merchant/admin/updateBindWxPromptStatus",
                    "/api/merchant/admin/updatePassword"
            ));
            sessionUser.setApiPaths(finalApiPaths);
        }
        //是否开启加解密
        if(SessionOperation.getConfiguration().isEnableSecret()){
            sessionUser.setSecret(RandomUtil.randomString(16));
            sessionUser.setSecretEnabled(true);
        }
        sessionUser.setDataInsIds(new ArrayList<>(sessionUser.getDataInsIds()));
        sessionUser.getDataInsIds().add(sessionUser.getInsId());
        stringRedisTemplate.opsForValue().set(RedisPrefix.MERCHANT_ADMIN_USER_TOKEN + token, JSONUtil.toJsonStr(sessionUser),loginTimeout, TimeUnit.MINUTES);
        //这个不需要放入缓存中去
        sessionUser.setAllPermission(allPermission);
        sessionUser.setCover(merchant.getCover());
        return sessionUser;
    }

    @Override
    public MerchantSessionUser loginByPhone(LoginByPhoneDto dto) {
        return null;
    }

    @Override
    public void signOut(String token) {
        if(StringUtils.isNotBlank(token)) {
            stringRedisTemplate.delete(RedisPrefix.MERCHANT_ADMIN_USER_TOKEN + token);
        }
    }
}
